#include <string.h>
#include "mhscpu.h"
#include "gpio.h"
#include "drv_gpio.h"

typedef enum {
	GPIO_Direction_IN		= 0x0,
	GPIO_Direction_OUT		= 0x1
} gpio_direction_t;

#define GPIO_NUMS			(8)

// GPIO_A, GPIO_B, GPIO_C, GPIO_D
#define GPIO_IODR_REG(GPIO_INDEX) 	((GPIOArray[GPIO_INDEX])->IODR)		// IO数据
#define GPIO_OEN_REG(GPIO_INDEX) 	((GPIOArray[GPIO_INDEX])->OEN)		// 方向
#define GPIO_PUE_REG(GPIO_INDEX) 	((GPIOArray[GPIO_INDEX])->PUE)		// 上拉
#define GPIO_BSRR_REG(GPIO_INDEX) 	((GPIOArray[GPIO_INDEX])->BSRR)		// 位控制

// GPIO
#define GPIO_WKUP_EN_REG(Base) 		((Base)->WAKE_EVEN_TYPE_EN)			// 唤醒源使能及GPIO口唤醒配置
#define GPIO_WKUP_GPIOAB_REG(Base) 	((Base)->WAKE_EVEN_SRC_L)			// 可唤醒IO口配置
#define GPIO_WKUP_GPIOCD_REG(Base) 	((Base)->WAKE_EVEN_SRC_H)
#define GPIO_SYS_CR1_REG(Base) 		((Base)->SYS_CR1)
#define GPIO_ALT_REG(GPIO_INDEX)	((GPIO)->ALT[GPIO_INDEX])

// INT
#define GPIO_INTP_TYPE_REG(GPIO_INDEX) 	(GPIO->INTP_TYPE_STA[GPIO_INDEX].INTP_TYPE)
#define GPIO_INTP_STA_REG(GPIO_INDEX) 	(GPIO->INTP_TYPE_STA[GPIO_INDEX].INTP_STA)

// 默认采用PP模式，OD模式需要在direction_output之前通过gpio_mux_config设置为OD_UP
static uint32_t	pin_od_mode_mask[GPIO_NUMS] = {0};
// 局部变量
GPIO_TypeDef* GPIOArray[GPIO_NUMS] = {GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH};

#define GPIO_PORT(n)    (((n) >> 4) & 0xf)      /* Get the GPIO Port */
#define GPIO_PIN(n)     ((0x01 << (n & 15)))    /* Get the GPIO Index */

void gpio_mux_config(unsigned gpio, unsigned mux, unsigned config)
{
	uint8_t pin_index = (gpio & 0x0f);

	if ((GPIO_ALT_REG(GPIO_PORT(gpio)) & (0x03 << (pin_index * 2))) != (mux << (pin_index * 2))) {
		GPIO_ALT_REG(GPIO_PORT(gpio)) &= ~(0x03 << (pin_index * 2));
		GPIO_ALT_REG(GPIO_PORT(gpio)) |= mux << (pin_index * 2);
	}
	
	if (mux == GPIO_MUX_ALT1) {
		pin_od_mode_mask[GPIO_PORT(gpio)] &= ~GPIO_PIN(gpio);

		if (config == GPIO_Mode_Out_PP) {
			// PP模式，通过控制IODR输出高低电平，输入必须浮空
			GPIO_IODR_REG(GPIO_PORT(gpio)) |= GPIO_PIN(gpio);
			GPIO_OEN_REG(GPIO_PORT(gpio)) &= ~GPIO_PIN(gpio);
			GPIO_PUE_REG(GPIO_PORT(gpio)) &= ~GPIO_PIN(gpio);
		} else if (config == GPIO_Mode_IN_FLOATING) {
			// 输入模式与IODR无关，但建议置高
			GPIO_IODR_REG(GPIO_PORT(gpio)) |= GPIO_PIN(gpio);
			GPIO_OEN_REG(GPIO_PORT(gpio)) |= GPIO_PIN(gpio);
			GPIO_PUE_REG(GPIO_PORT(gpio)) &= ~GPIO_PIN(gpio);
		} else if (config == GPIO_Mode_IPU) {
			// 输入模式与IODR无关，但建议置高
			GPIO_IODR_REG(GPIO_PORT(gpio)) |= GPIO_PIN(gpio);
			GPIO_OEN_REG(GPIO_PORT(gpio)) |= GPIO_PIN(gpio);
			GPIO_PUE_REG(GPIO_PORT(gpio)) |= GPIO_PIN(gpio);
		} else if (config == GPIO_Mode_Out_OD) {
			// 开漏模式下IODR必须要置0，否则无低电平输出，默认高电平
			GPIO_IODR_REG(GPIO_PORT(gpio)) &= ~GPIO_PIN(gpio);
			GPIO_OEN_REG(GPIO_PORT(gpio)) |= GPIO_PIN(gpio);
			GPIO_PUE_REG(GPIO_PORT(gpio)) &= ~GPIO_PIN(gpio);
			pin_od_mode_mask[GPIO_PORT(gpio)] |= GPIO_PIN(gpio);
		} else if (config == GPIO_Mode_Out_OD_PU) {
			// 开漏模式下IODR必须要置0，否则无低电平输出，默认高电平
			GPIO_IODR_REG(GPIO_PORT(gpio)) &= ~GPIO_PIN(gpio);
			GPIO_OEN_REG(GPIO_PORT(gpio)) |= GPIO_PIN(gpio);
			GPIO_PUE_REG(GPIO_PORT(gpio)) |= GPIO_PIN(gpio);
			pin_od_mode_mask[GPIO_PORT(gpio)] |= GPIO_PIN(gpio);
		}
	}
}

void gpio_direction_inoutput(unsigned gpio, unsigned direction)
{
	if (pin_od_mode_mask[GPIO_PORT(gpio)] & GPIO_PIN(gpio)) {
		if (direction != GPIO_Direction_OUT) {
			GPIO_OEN_REG(GPIO_PORT(gpio)) |= GPIO_PIN(gpio);
		}
	} else {
		if (direction == GPIO_Direction_OUT) {
			gpio_mux_config(gpio, GPIO_MUX_ALT1, GPIO_Mode_Out_PP);	// 默认gpio属性PP
		} else {
			gpio_mux_config(gpio, GPIO_MUX_ALT1, GPIO_Mode_IPU);
		}
	}
}

int gpio_get_value(unsigned gpio)
{
	return (((GPIO_IODR_REG(GPIO_PORT(gpio)) >> 16) & GPIO_PIN(gpio)) ? 0x01 : 0x00);
}

void gpio_set_value(unsigned gpio, int value)
{
	if (pin_od_mode_mask[GPIO_PORT(gpio)] & GPIO_PIN(gpio)) {
		// 开漏输出
		if (value) {
			GPIO_OEN_REG(GPIO_PORT(gpio)) |= GPIO_PIN(gpio);
		} else {
			GPIO_OEN_REG(GPIO_PORT(gpio)) &= ~(GPIO_PIN(gpio));
		}
	} else {
		if (value) {
			GPIO_BSRR_REG(GPIO_PORT(gpio)) |= GPIO_PIN(gpio);
		} else {
			GPIO_BSRR_REG(GPIO_PORT(gpio)) |= (GPIO_PIN(gpio) << 16);
		}
	}
}

void gpio_direction_input_only(unsigned gpio)
{
	gpio_set_value(gpio, 1);
	gpio_direction_inoutput(gpio, GPIO_Direction_IN);
}

void gpio_direction_output_only(unsigned gpio)
{
	gpio_direction_inoutput(gpio, GPIO_Direction_OUT);
	gpio_set_value(gpio, 1);
}

int gpio_direction_input(unsigned gpio)
{
	gpio_direction_input_only(gpio);
	return gpio_get_value(gpio);
}

int gpio_direction_output(unsigned gpio, int value)
{
	gpio_direction_output_only(gpio);
	gpio_set_value(gpio, value);
	return 0;
}

void gpio_free(unsigned gpio)
{

}

int gpio_request(unsigned gpio, const char *tag)
{
	return 0;
}

unsigned char get_gpio_interrupt_flag(unsigned gpio)
{
	return ((GPIO_INTP_STA_REG(GPIO_PORT(gpio)) & (0x01 << GPIO_PIN(gpio))) ? 0x01 : 0x00);
}

void clear_gpio_interrupt_flag(unsigned gpio)
{
	GPIO_INTP_STA_REG(GPIO_PORT(gpio)) |= 0x01 << GPIO_PIN(gpio);
}


void gpio_interrupt_config(unsigned gpio, unsigned interrupt_type)
{
	uint8_t pin_index = (gpio & 0x0f);

	GPIO_INTP_TYPE_REG(GPIO_PORT(gpio)) &= ~(0x03 << (pin_index * 2));
	GPIO_INTP_TYPE_REG(GPIO_PORT(gpio)) |= interrupt_type << (pin_index * 2);
}



